import * as THREE from 'three';
// import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';
// import {FlyControls} from 'three/examples/jsm/controls/FlyControls.js';
import {TWEEN} from 'three/examples/jsm/libs/tween.module.min';
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader';
// import {GLTFExporter} from 'three/examples/jsm/exporters/GLTFExporter';
import {DRACOLoader} from 'three/examples/jsm/loaders/DRACOLoader';
import Stats from 'three/examples/jsm/libs/stats.module.js';
import {GUI} from 'three/examples/jsm/libs/dat.gui.module.js';
import {baseUrl} from '../../js/base.js';
import {OrbitControls} from './no_pack_js/OrbitControls';
import {drawPoint, drawLine, drawFace, drawConcaveFace, setModelCenter, cameraMoveLookModel, canvasResize} from './no_pack_js/utils';
import api from './api'

import '../../css/base.css';

let body = document.body,
	canvas = document.getElementById('canvas');
let renderer, scene, camera; // 场景、相机、渲染器

let stats, orbitControls, flyControls, clock = new THREE.Clock(); //

// 建筑物楼层每层数据
let buildingParams = window.buildingParams = {0: {floorNumber: 0, floorH: 6, position: new THREE.Vector3(), object: null, userData: null}};

let buildingGroup = new THREE.Group();
let mouse = new THREE.Vector2(), // 指针在屏幕上的二维位置
	point = new THREE.Vector3(), // 指针在三维上的位置
	mouseOnDownPosition = new THREE.Vector2(); // 指针点击在屏幕上的二维位置
let raycaster = new THREE.Raycaster(); // 射线

let faceGroup = new THREE.Group(),  // 画面的组
	face,
	lineGroup = new THREE.Group(), // 画线的组
	line, // 画玩的线对象
	mousePoint; // 标点是跟随鼠标的点
let pointsList = []; // 画点、线、面临时存放的点数据

// gui 操作参数
const guiParams = window.guiParams = {
	open: true,
	drawPoint: false,
	drawLine: false,
	drawFace: false,
	modelCenter: false,
	setUpFloor: false,
	selectFloorActive: 0, // 选中的楼层
	selectFloor: [0, 1, 2, 3, 4],
}

// 单层楼层点、线、面的数据
let floorParams = window.floorParams = {
	// 单层楼点数据
	points: [{'name': '0', 'floorNumber': 0, 'point': [59, 1, -1]}],
	// 单层楼线数据
	lines: [{'name': '0', 'floorNumber': 0, 'points': [[56, 1, -47], [3, 1, 26], [-66, 1, -25]], 'object': null}],
	// 单层楼面数据
	faces: [{'name': '0', 'floorNumber': 0, 'points': [[48, 1, 25], [43, 1, 5], [9, 1, 29], [-14, 1, 40], [-14, 1, 40]], 'object': null}]
}

// 跨层楼层点、线、面的数据
let floorsParams = window.floorsParams = {
	// 跨层楼点数据
	points: [{name: '', points: [{name: '', floorNumber: 0, point: new THREE.Vector3()}]}],
	// 跨层楼线数据
	lines: [{name: '', points: [{name: '', floorName: 0, point: new THREE.Vector3()}]}],
	// 跨层区域数据
	box: [{name: '', startFloorNumber: 0, endFloorNumber: 1, points: [new THREE.Vector3()]}]
}

// 初始化场景
function initCSR() {
	const w = canvas.offsetWidth;
	const h = canvas.offsetHeight;

	camera = new THREE.PerspectiveCamera(75, w / h, 0.1, 10000);
	// camera = new THREE.OrthographicCamera(-w, w, h, -h, 1, 1000);
	camera.position.set(0, 60, 100);

	window.scene = scene = new THREE.Scene();
	// scene.add(new THREE.AxesHelper(1000))
	// scene.background = new THREE.Color(0xefefef);
	// scene.fog = new THREE.Fog( 0xefefef, 100, 500 );

	renderer = new THREE.WebGLRenderer({canvas, antialias: true, alpha: true, premultipliedAlpha: true});
	renderer.outputEncoding = THREE.sRGBEncoding;
	renderer.sortObjects = false;
	camera.updateProjectionMatrix();
	renderer.setPixelRatio(window.devicePixelRatio);
	renderer.setSize(w, h, false);
}

// 初始化灯光
function initLight() {
	const ambientLight = new THREE.AmbientLight(0xFFFFFF, .6);
	scene.add(ambientLight);

	const directionalLight = new THREE.DirectionalLight(0xFFFFFF, 1);
	directionalLight.position.set(-10, 10, -10);
	scene.add(directionalLight);
}

// 初始化模型
function initModel() {
	const gltfLoader = new GLTFLoader();
	const dracoLoader = new DRACOLoader();
	dracoLoader.setDecoderPath('/static/examples/js/libs/draco/');
	gltfLoader.setDRACOLoader(dracoLoader);
	let textureLoader = new THREE.TextureLoader();
	const texture = textureLoader.load(baseUrl + 'static/images/floor.jpeg');
	gltfLoader.load(baseUrl + 'static/models/demo-min.glb', onLoad, onProgress, onError);

	function onLoad(gltf) {
		const gltfScene = gltf.scene;
		gltfScene.children.forEach(value => {
			value.traverse(object => {
				object.name = value.name
			})
			buildingParams[value.name] = {floorNumber: value.name, position: new THREE.Vector3(), object: value.clone(), userData: value.userData};
			buildingGroup.add(buildingParams[value.name].object);
			buildingGroup.name = 'buildingGroup';
		});

		// 添加到射线识别的组
		scene.add(buildingGroup);

		setTimeout(()=>{
			const object = scene.getObjectByName('buildingGroup')
			scene.add(new THREE.Box3Helper(new THREE.Box3().setFromObject(object), 0xFF0000));


			const box = new THREE.Box3().setFromObject(object.children[0]);
			const boxSize = box.getSize();
			console.log('boxSize', boxSize)
			const center = box.getCenter(new THREE.Vector3());
			console.log('center', center)
		}, 2000)

		if (guiParams.open) setTimeout(() => openOrCloseFloor(true))
	}

	function onProgress(xhr) {
		// console.log(xhr)
	}

	function onError(err) {
		console.error(err)
	}
}

// 初始化控制器
function initControls() {
	{
		orbitControls = new OrbitControls(camera, canvas);
		orbitControls.enableDamping = true;
		orbitControls.screenSpacePanning = true;
		orbitControls.enableKeys = false;
		orbitControls.mouseButtons = {
			LEFT: THREE.MOUSE.PAN,
			MIDDLE: THREE.MOUSE.DOLLY,
			RIGHT: THREE.MOUSE.ROTATE
		};
		window.orbitControls = orbitControls;
	}

	{
		// flyControls = new FlyControls(camera, canvas);
		// flyControls.movementSpeed = 100
		// flyControls.rollSpeed = 1
		// flyControls.dragToLook = true
	}
}

// 初始化帮手
function initHelper() {
	stats = new Stats();
	body.appendChild(stats.domElement);

	const gui = new GUI();

	gui.add(guiParams, 'open').onChange((boolean) => openOrCloseFloor(boolean)).listen()
	gui.add(guiParams, 'modelCenter').onChange(() => {
		// setModelCenter(camera, buildingGroup)
		cameraMoveLookModel(camera, buildingGroup)
	}).listen();

	// 画一个跟随鼠标的点
	mousePoint = drawPoint(scene, new THREE.Vector3());

	gui.add(guiParams, 'drawPoint').onChange((boolean) => {
		if (boolean) mousePoint.material.color = new THREE.Color(0xFF0000);
	}).listen();
	gui.add(guiParams, 'drawLine').onChange((boolean) => {
		if (boolean) {
			pointsList = []; // 清空

			const objectByName = buildingGroup.getObjectByName(guiParams.selectFloorActive);
			// 线信息处理
			line = new THREE.Group();
			line.name = 'line';
			lineGroup = objectByName.getObjectByName('floorLineGroup');
			if (!lineGroup) { // 如果保存这些线的组不存在则创建
				lineGroup = new THREE.Group();
				lineGroup.name = 'floorLineGroup';
				objectByName.add(lineGroup);
			}

			mousePoint.material.color = new THREE.Color(0xFFFF00);
		} else {
			lineGroup = null;
		}
	}).listen();
	gui.add(guiParams, 'drawFace').onChange((boolean) => {
		if (boolean) {
			pointsList = [];

			const objectByName = buildingGroup.getObjectByName(guiParams.selectFloorActive);
			// 面信息处理
			face = new THREE.Group();
			face.name = 'face';
			faceGroup = objectByName.getObjectByName('floorFaceGroup');
			if (!faceGroup) { // 如果保存这些线的组不存在则创建
				faceGroup = new THREE.Group();
				faceGroup.name = 'floorLineGroup';
				objectByName.add(faceGroup);
			}

			mousePoint.material.color = new THREE.Color(0x0000FF);
		} else {
			faceGroup = null;
		}
	}).listen();

	gui.add(guiParams, 'selectFloorActive').options(guiParams.selectFloor).onChange(number => {
		console.log(number);
		guiParams.selectFloorActive = number;

		const objectByName = buildingGroup.getObjectByName(guiParams.selectFloorActive);
		pointsList = [];
		// 线信息处理
		line = new THREE.Group();
		line.name = 'line';
		lineGroup = objectByName.getObjectByName('floorLineGroup');
		if (!lineGroup) { // 如果保存这些线的组不存在则创建
			lineGroup = new THREE.Group();
			lineGroup.name = 'floorLineGroup';
			objectByName.add(lineGroup);
		}
		// 面信息处理
		face = new THREE.Group();
		face.name = 'face';
		faceGroup = objectByName.getObjectByName('floorFaceGroup');
		if (!faceGroup) { // 如果保存这些线的组不存在则创建
			faceGroup = new THREE.Group();
			faceGroup.name = 'floorLineGroup';
			objectByName.add(faceGroup);
		}
	}).listen();
}

// 展开合并楼层
function openOrCloseFloor(boolean) {
	buildingGroup.children.forEach((value, index) => {
		new TWEEN.Tween({position: value.position.y}).to({
			position: boolean ? value.position.y + value.name * 30 : value.position.y - value.name * 30
		}, 500).onUpdate(val => {
			value.position.setY(val.position);
		}).onComplete(value => {
		}).start();
	});
}

// 添加监听器
function addEL() {
	window.addEventListener('resize', () => canvasResize(renderer, camera, canvas.offsetWidth, canvas.offsetHeight))
	canvas.addEventListener('pointerdown', mouseDown, false);
	canvas.addEventListener('pointermove', mouseMove, false);
	canvas.addEventListener('pointerup', mouseUp, false);
	canvas.addEventListener('mouseup', (ev) => {
		console.log(JSON.stringify(pointsList));
		if (ev.button === 2) {

			// 标点 guiParams.drawPoint 是否开启标点
			if (guiParams.drawPoint) {
				const currentlyClickedFloor = buildingGroup.getObjectByName(guiParams.selectFloorActive);
				let points = currentlyClickedFloor.getObjectByName('points');
				if (!points) {
					points = new THREE.Group();
					points.name = 'points';
					currentlyClickedFloor.add(points);
				}
				drawPoint(points, new THREE.Vector3().copy(point.setY(1)));

				floorParams.points.push({
					name: guiParams.selectFloorActive,
					floorNumber: +guiParams.selectFloorActive,
					point: new THREE.Vector3().copy(point.setY(1)),
				});
			}
			if (guiParams.drawLine) {
				floorParams.lines.push({
					name: guiParams.selectFloorActive,
					floorNumber: +guiParams.selectFloorActive,
					points: pointsList,
					object: null
				});
			}
			if (guiParams.drawFace) {
				floorParams.faces.push({
					name: guiParams.selectFloorActive,
					floorNumber: +guiParams.selectFloorActive,
					points: pointsList,
					object: null
				});
			}
			mousePoint.visible = false

			guiParams.selectFloorActive = 0
			guiParams.drawPoint = false;
			guiParams.drawLine = false;
			guiParams.drawFace = false;
		}
	})
}

// 按下
function mouseDown(e) {
	//
	mouse.x = (e.offsetX / canvas.offsetWidth) * 2 - 1;
	mouse.y = -(e.offsetY / canvas.offsetHeight) * 2 + 1;
	//
	raycaster.setFromCamera(mouse, camera);
	let intersectObjects = raycaster.intersectObjects(buildingGroup.children, true);
	// 有拾取到东西
	if (intersectObjects.length) {
		const object = intersectObjects[0];
		// 保存鼠标按下的点位置-----为单击操作使用
		mouseOnDownPosition.copy(mouse);

		// 画线的第一个点
		if (guiParams.drawLine || guiParams.drawFace) {
			console.log(pointsList);
			if (guiParams.selectFloorActive === object.object.name) {
				if (pointsList.length < 1) {
					pointsList = [];
					pointsList.push([object.point.x, 1, object.point.z]);
					pointsList.push([object.point.x, 1, object.point.z]);
				} else {
					pointsList.push([object.point.x, 1, object.point.z]);
				}
			}
		}
	}
}

// 移动
function mouseMove(e) {
	//
	mouse.x = (e.offsetX / canvas.offsetWidth) * 2 - 1;
	mouse.y = -(e.offsetY / canvas.offsetHeight) * 2 + 1;
	//
	raycaster.setFromCamera(mouse, camera);
	let intersectObjects = raycaster.intersectObjects(buildingGroup.children, true);

	// 有拾取到东西
	if (intersectObjects.length) {
		const object = intersectObjects[0];

		// 需要 让点 跟随鼠标
		if (guiParams.drawPoint || guiParams.drawLine || guiParams.drawFace) {
			if (mousePoint) {
				mousePoint.visible = true;
				mousePoint.position.copy(object.point.setY(object.point.y + 1));
			} else {
				mousePoint = drawPoint(scene, new THREE.Vector3());
			}
		}

		// 画线
		if (guiParams.drawLine) {
			pointsList.pop();
			pointsList.push([object.point.x, 1, object.point.z]);
			lineGroup.remove(line);
			line = drawLine(lineGroup, pointsList);
		}

		// 画面
		if (guiParams.drawFace) {
			pointsList.pop();
			pointsList.push([object.point.x, 1, object.point.z]);
			faceGroup.remove(face);
			face = drawConcaveFace(faceGroup, pointsList);
		}

	} else {
		// 隐藏跟随鼠标的点
		if (mousePoint) mousePoint.visible = false;
	}
}

// 弹起
function mouseUp(e) {
	//
	mouse.x = (e.offsetX / canvas.offsetWidth) * 2 - 1;
	mouse.y = -(e.offsetY / canvas.offsetHeight) * 2 + 1;
	//
	raycaster.setFromCamera(mouse, camera);
	let intersectObjects = raycaster.intersectObjects(buildingGroup.children, true);

	// 有拾取到东西
	if (intersectObjects.length) {
		const object = intersectObjects[0];
		console.log(object.object.name)
		// 单击了同一个位置
		if (mouseOnDownPosition.equals(mouse)) {
			// 设置焦点
			guiParams.selectFloorActive = object.object.name; // active状态的楼层
			point.copy(object.point)
		}
	}
}

// 渲染
function render() {
	if (orbitControls) orbitControls.update();
	if (flyControls) flyControls.update(clock.getDelta());
	if (TWEEN) TWEEN.update();
	if (stats) stats.update();

	renderer.render(scene, camera);
}

// 循环渲染
function animation() {
	requestAnimationFrame(animation);
	render();
}

// 启动
(function () {
	initCSR();
	initLight();
	initHelper();
	initModel();
	initControls();
	addEL();
	// setTimeout(() => render(), 100)
	animation();
})();

// 获取并初始化-点数据
function getAndSetPoints() {
	floorParams.points.forEach(value => {
		const buildingParam = buildingParams[value.name];
		if (buildingParam.object.getObjectByName('points')) {
			const points = new THREE.Group();
			points.name = 'points';
		}
		drawPoint(buildingParam.object, new THREE.Vector3(...value.point));
	});
}

// 获取并初始化-线数据
function getAndSetLine() {
	floorParams.lines.forEach(value => {
		const buildingParam = buildingParams[value.name];
		if (buildingParam.object.getObjectByName('floorLineGroup')) {
			const floorLineGroup = new THREE.Group();
			floorLineGroup.name = 'floorLineGroup';
		}
		drawLine(buildingParam.object, value.points);
	});
}

// 获取并初始化-面数据
function getAndSetFace() {
	floorParams.faces.forEach(value => {
		const buildingParam = buildingParams[value.name];
		if (buildingParam.object.getObjectByName('floorFaceGroup')) {
			const floorLineGroup = new THREE.Group();
			floorLineGroup.name = 'floorFaceGroup';
		}
		drawLine(buildingParam.object, value.points, {isLine: true});
		drawConcaveFace(buildingParam.object, value.points);
	});
}

const interval = setInterval(async () => {
	if (buildingGroup.getObjectByName('2')) {
		const queryPoint = await api.queryPoint()
		const queryLine = await api.queryLine()
		const queryFace = await api.queryFace()

		queryPoint.map(value => value.name = value.floorNumber)
		floorParams.points = queryPoint
		queryLine.map(value => value.name = value.floorNumber)
		floorParams.lines = queryLine
		queryFace.map(value => value.name = value.floorNumber)
		floorParams.faces = queryFace

		getAndSetPoints();
		getAndSetLine();
		getAndSetFace();
		clearInterval(interval);
	}
}, 200);


